Skip to content

[lldb] Load scripts from code signed dSYM bundles - #189444

Merged
JDevlieghere merged 1 commit into
llvm:mainfrom
JDevlieghere:trust-codesigned-dsyms
Apr 3, 2026
Merged

[lldb] Load scripts from code signed dSYM bundles#189444
JDevlieghere merged 1 commit into
llvm:mainfrom
JDevlieghere:trust-codesigned-dsyms

Conversation

@JDevlieghere

Copy link
Copy Markdown
Member

LLDB automatically discovers, but doesn't automatically load, scripts in the dSYM bundle. This is to prevent running untrusted code. Users can choose to import the script manually or toggle a global setting to override this policy. This isn't a great user experience: the former quickly becomes tedious and the latter leads to decreased security.

This PR offers a middle ground that allows LLDB to automatically load scripts from trusted dSYM bundles. Trusted here means that the bundle was signed with a certificate trusted by the system. This can be a locally created certificate (but not an ad-hoc certificate) or a certificate from a trusted vendor.

@llvmbot

llvmbot commented Mar 30, 2026

Copy link
Copy Markdown
Member

@llvm/pr-subscribers-lldb

Author: Jonas Devlieghere (JDevlieghere)

Changes

LLDB automatically discovers, but doesn't automatically load, scripts in the dSYM bundle. This is to prevent running untrusted code. Users can choose to import the script manually or toggle a global setting to override this policy. This isn't a great user experience: the former quickly becomes tedious and the latter leads to decreased security.

This PR offers a middle ground that allows LLDB to automatically load scripts from trusted dSYM bundles. Trusted here means that the bundle was signed with a certificate trusted by the system. This can be a locally created certificate (but not an ad-hoc certificate) or a certificate from a trusted vendor.


Full diff: https://github.com/llvm/llvm-project/pull/189444.diff

12 Files Affected:

  • (modified) lldb/include/lldb/Host/macosx/HostInfoMacOSX.h (+4)
  • (modified) lldb/include/lldb/Target/Platform.h (+5)
  • (modified) lldb/source/Core/Module.cpp (+14-8)
  • (modified) lldb/source/Host/macosx/objcxx/CMakeLists.txt (+3)
  • (modified) lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm (+27)
  • (modified) lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp (+34)
  • (modified) lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h (+2)
  • (modified) lldb/source/Target/Platform.cpp (+2)
  • (added) lldb/test/API/macosx/dsym_codesign/Makefile (+3)
  • (added) lldb/test/API/macosx/dsym_codesign/TestdSYMCodesign.py (+78)
  • (added) lldb/test/API/macosx/dsym_codesign/dsym_script.py (+4)
  • (added) lldb/test/API/macosx/dsym_codesign/main.c (+1)
diff --git a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
index ed00c44df4bdc..fc676622a2d65 100644
--- a/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
+++ b/lldb/include/lldb/Host/macosx/HostInfoMacOSX.h
@@ -58,6 +58,10 @@ class HostInfoMacOSX : public HostInfoPosix {
   static bool SharedCacheIndexFiles(FileSpec &filepath, UUID &uuid,
                                     lldb::SymbolSharedCacheUse sc_mode);
 
+  /// Check whether a bundle at the given path has a valid code signature that
+  /// chains to a trusted anchor in the system trust store.
+  static bool IsBundleCodeSignTrusted(llvm::StringRef bundle_path);
+
 protected:
   static bool ComputeSupportExeDirectory(FileSpec &file_spec);
   static void ComputeHostArchitectureSupport(ArchSpec &arch_32,
diff --git a/lldb/include/lldb/Target/Platform.h b/lldb/include/lldb/Target/Platform.h
index 3d0776d95b539..626838b3e86e1 100644
--- a/lldb/include/lldb/Target/Platform.h
+++ b/lldb/include/lldb/Target/Platform.h
@@ -294,6 +294,11 @@ class Platform : public PluginInterface {
   static FileSpecList LocateExecutableScriptingResourcesFromSafePaths(
       Stream &feedback_stream, FileSpec module_spec, const Target &target);
 
+  /// Returns true if the module's symbol file (e.g. a dSYM bundle) is
+  /// code-signed with a trusted signature. Used to decide whether to
+  /// auto-loaded scripts.
+  virtual bool IsSymbolFileTrusted(Module &module);
+
   /// \param[in] module_spec
   ///     The ModuleSpec of a binary to find.
   ///
diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp
index aad23c0486805..84ba6b801846d 100644
--- a/lldb/source/Core/Module.cpp
+++ b/lldb/source/Core/Module.cpp
@@ -1479,9 +1479,10 @@ bool Module::LoadScriptingResourceInTarget(Target *target, Status &error) {
       continue;
 
     if (should_load == eLoadScriptFromSymFileWarn) {
-      // clang-format off
-      debugger.ReportWarning(
-          llvm::formatv(
+      if (!platform_sp->IsSymbolFileTrusted(*this)) {
+        debugger.ReportWarning(
+            llvm::formatv(
+                // clang-format off
 R"('{0}' contains a debug script. To run this script in this debug session:
 
     command script import "{1}"
@@ -1490,12 +1491,17 @@ To run all discovered debug scripts in this session:
 
     settings set target.load-script-from-symbol-file true
 )",
-              GetFileSpec().GetFileNameStrippingExtension(),
-              scripting_fspec.GetPath()),
-          debugger.GetID());
-      // clang-format on
+                // clang-format on
+                GetFileSpec().GetFileNameStrippingExtension(),
+                scripting_fspec.GetPath()),
+            debugger.GetID());
 
-      return false;
+        return false;
+      }
+
+      LLDB_LOG(GetLog(LLDBLog::Modules),
+               "Auto-loading {0} from trusted symbol file",
+               scripting_fspec.GetPath());
     }
 
     LLDB_LOG(GetLog(LLDBLog::Modules), "Auto-loading {0}",
diff --git a/lldb/source/Host/macosx/objcxx/CMakeLists.txt b/lldb/source/Host/macosx/objcxx/CMakeLists.txt
index 1d7573335b8ec..81c07e01873d3 100644
--- a/lldb/source/Host/macosx/objcxx/CMakeLists.txt
+++ b/lldb/source/Host/macosx/objcxx/CMakeLists.txt
@@ -18,6 +18,9 @@ add_lldb_library(lldbHostMacOSXObjCXX NO_PLUGIN_DEPENDENCIES
     ${EXTRA_LIBS}
   )
 
+find_library(SECURITY_FRAMEWORK Security)
+target_link_libraries(lldbHostMacOSXObjCXX PRIVATE ${SECURITY_FRAMEWORK})
+
 target_compile_options(lldbHostMacOSXObjCXX PRIVATE
   -fno-objc-exceptions
   -Wno-deprecated-declarations)
diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
index fd0c11197fc66..91df1fa4174b8 100644
--- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
+++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
@@ -44,6 +44,7 @@
 #include <AvailabilityMacros.h>
 #include <CoreFoundation/CoreFoundation.h>
 #include <Foundation/Foundation.h>
+#include <Security/Security.h>
 #include <mach-o/dyld.h>
 #if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \
     MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0
@@ -1096,3 +1097,29 @@ static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)(
         uuid, filepath.GetPath());
   return false;
 }
+
+bool HostInfoMacOSX::IsBundleCodeSignTrusted(llvm::StringRef bundle_path) {
+  CFURLRef url = CFURLCreateFromFileSystemRepresentation(
+      kCFAllocatorDefault, reinterpret_cast<const UInt8 *>(bundle_path.data()),
+      bundle_path.size(), /*isDirectory=*/true);
+  if (!url)
+    return false;
+  auto url_cleanup = llvm::make_scope_exit([&]() { CFRelease(url); });
+
+  SecStaticCodeRef static_code = nullptr;
+  if (SecStaticCodeCreateWithPath(url, kSecCSDefaultFlags, &static_code) !=
+      errSecSuccess)
+    return false;
+  auto code_cleanup = llvm::make_scope_exit([&]() { CFRelease(static_code); });
+
+  // Check that the signature chains to a trusted root CA.
+  SecRequirementRef requirement = nullptr;
+  if (SecRequirementCreateWithString(CFSTR("anchor trusted"),
+                                     kSecCSDefaultFlags,
+                                     &requirement) != errSecSuccess)
+    return false;
+  auto req_cleanup = llvm::make_scope_exit([&]() { CFRelease(requirement); });
+
+  return SecStaticCodeCheckValidity(static_code, kSecCSDefaultFlags,
+                                    requirement) == errSecSuccess;
+}
diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
index 98f0025303ac1..587c6ee166f3e 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp
@@ -50,6 +50,7 @@
 #include "llvm/Support/VersionTuple.h"
 
 #if defined(__APPLE__)
+#include "lldb/Host/macosx/HostInfoMacOSX.h"
 #include <TargetConditionals.h>
 #endif
 
@@ -291,6 +292,39 @@ FileSpecList PlatformDarwin::LocateExecutableScriptingResourcesForPlatform(
   return {};
 }
 
+bool PlatformDarwin::IsSymbolFileTrusted(Module &module) {
+#if defined(__APPLE__)
+  SymbolFile *symfile = module.GetSymbolFile();
+  if (!symfile)
+    return false;
+
+  ObjectFile *objfile = symfile->GetObjectFile();
+  if (!objfile)
+    return false;
+
+  std::string symfile_path = objfile->GetFileSpec().GetPath();
+  llvm::StringRef path_ref(symfile_path);
+
+  // Find the .dSYM bundle root from the symfile path, which is typically
+  // .dSYM/Contents/Resources/DWARF/<name>.
+  auto pos = path_ref.find(".dSYM/");
+  if (pos == llvm::StringRef::npos)
+    return false;
+
+  std::string bundle_path = path_ref.substr(0, pos + 5).str();
+
+  if (HostInfoMacOSX::IsBundleCodeSignTrusted(bundle_path)) {
+    LLDB_LOG(GetLog(LLDBLog::Modules),
+             "dSYM bundle '{0}' has valid trusted code signature", bundle_path);
+    return true;
+  }
+
+  return false;
+#else
+  return false;
+#endif
+}
+
 Status PlatformDarwin::ResolveSymbolFile(Target &target,
                                          const ModuleSpec &sym_spec,
                                          FileSpec &sym_file) {
diff --git a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
index 2b0b7ad4b827d..2e6de26d59c41 100644
--- a/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
+++ b/lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h
@@ -70,6 +70,8 @@ class PlatformDarwin : public PlatformPOSIX {
   FileSpecList LocateExecutableScriptingResourcesForPlatform(
       Target *target, Module &module_spec, Stream &feedback_stream) override;
 
+  bool IsSymbolFileTrusted(Module &module) override;
+
   Status GetSharedModule(const ModuleSpec &module_spec, Process *process,
                          lldb::ModuleSP &module_sp,
                          llvm::SmallVectorImpl<lldb::ModuleSP> *old_modules,
diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp
index c4bcdfab268c9..d39e2ef8665d0 100644
--- a/lldb/source/Target/Platform.cpp
+++ b/lldb/source/Target/Platform.cpp
@@ -157,6 +157,8 @@ Status Platform::GetFileWithUUID(const FileSpec &platform_file,
   return Status();
 }
 
+bool Platform::IsSymbolFileTrusted(Module &module) { return false; }
+
 FileSpecList Platform::LocateExecutableScriptingResourcesFromSafePaths(
     Stream &feedback_stream, FileSpec module_spec, const Target &target) {
   assert(module_spec);
diff --git a/lldb/test/API/macosx/dsym_codesign/Makefile b/lldb/test/API/macosx/dsym_codesign/Makefile
new file mode 100644
index 0000000000000..10495940055b6
--- /dev/null
+++ b/lldb/test/API/macosx/dsym_codesign/Makefile
@@ -0,0 +1,3 @@
+C_SOURCES := main.c
+
+include Makefile.rules
diff --git a/lldb/test/API/macosx/dsym_codesign/TestdSYMCodesign.py b/lldb/test/API/macosx/dsym_codesign/TestdSYMCodesign.py
new file mode 100644
index 0000000000000..2376579ff69bd
--- /dev/null
+++ b/lldb/test/API/macosx/dsym_codesign/TestdSYMCodesign.py
@@ -0,0 +1,78 @@
+import os
+import shutil
+import subprocess
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+def has_lldb_codesign():
+    """Check if the lldb_codesign certificate is available."""
+    try:
+        result = subprocess.run(
+            [
+                "security",
+                "find-certificate",
+                "-c",
+                "lldb_codesign",
+                "/Library/Keychains/System.keychain",
+            ],
+            capture_output=True,
+        )
+        return result.returncode == 0
+    except FileNotFoundError:
+        return False
+
+
+@skipUnlessDarwin
+class TestdSYMCodesign(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+    SHARED_BUILD_TESTCASE = False
+
+    def build_dsym_with_script(self):
+        self.build(debug_info="dsym")
+        exe = self.getBuildArtifact("a.out")
+        dsym = self.getBuildArtifact("a.out.dSYM")
+        python_dir = os.path.join(dsym, "Contents", "Resources", "Python")
+        os.makedirs(python_dir, exist_ok=True)
+        shutil.copy(
+            os.path.join(self.getSourceDir(), "dsym_script.py"),
+            os.path.join(python_dir, "a.py"),
+        )
+        return exe, dsym
+
+    def test_adhoc_signed_dsym_warns(self):
+        """An ad-hoc signed dSYM should still show the warning because the
+        signature doesn't chain to a trusted root CA."""
+        exe, dsym = self.build_dsym_with_script()
+        subprocess.check_call(["codesign", "-f", "-s", "-", dsym])
+
+        self.runCmd("settings set target.load-script-from-symbol-file warn")
+        self.createTestTarget(file_path=exe)
+
+        self.expect(
+            "script -- print('SENTINEL')",
+            substrs=["SENTINEL"],
+        )
+        # The script should NOT have been loaded.
+        self.assertFalse(
+            hasattr(lldb, "_dsym_codesign_test_loaded"),
+            "Script should not auto-load from ad-hoc signed dSYM",
+        )
+
+    @unittest.skipUnless(has_lldb_codesign(), "requires lldb_codesign certificate")
+    def test_trusted_signed_dsym_auto_loads(self):
+        """A dSYM signed with the trusted lldb_codesign certificate should
+        auto-load scripts even with the default 'warn' setting."""
+        exe, dsym = self.build_dsym_with_script()
+        subprocess.check_call(["codesign", "-f", "-s", "lldb_codesign", dsym])
+
+        self.runCmd("settings set target.load-script-from-symbol-file warn")
+        self.createTestTarget(file_path=exe)
+
+        # The script sets a marker attribute on the lldb module.
+        self.assertTrue(
+            getattr(lldb, "_dsym_codesign_test_loaded", False),
+            "Script should auto-load from trusted signed dSYM",
+        )
diff --git a/lldb/test/API/macosx/dsym_codesign/dsym_script.py b/lldb/test/API/macosx/dsym_codesign/dsym_script.py
new file mode 100644
index 0000000000000..2dff8f5be43a4
--- /dev/null
+++ b/lldb/test/API/macosx/dsym_codesign/dsym_script.py
@@ -0,0 +1,4 @@
+import lldb
+
+def __lldb_init_module(debugger, internal_dict):
+    lldb._dsym_codesign_test_loaded = True
diff --git a/lldb/test/API/macosx/dsym_codesign/main.c b/lldb/test/API/macosx/dsym_codesign/main.c
new file mode 100644
index 0000000000000..78f2de106c92b
--- /dev/null
+++ b/lldb/test/API/macosx/dsym_codesign/main.c
@@ -0,0 +1 @@
+int main(void) { return 0; }

@github-actions

github-actions Bot commented Mar 30, 2026

Copy link
Copy Markdown

✅ With the latest revision this PR passed the Python code formatter.

@JDevlieghere
JDevlieghere force-pushed the trust-codesigned-dsyms branch from e9cca21 to 174e470 Compare March 30, 2026 18:25
@jimingham

Copy link
Copy Markdown
Contributor

If I'm reading this correctly, setting the script loading to off makes us never source script files. Setting it to warn now sources in "trusted" script files and warns about others. And setting it to on always sources.

So that's a change of behavior in warn which seems the right choice, but I don't see that change documented anywhere in this PR. There should be something in the help string for the setting.

Other than that this makes sense to me.

@JDevlieghere

Copy link
Copy Markdown
Member Author

If I'm reading this correctly, setting the script loading to off makes us never source script files. Setting it to warn now sources in "trusted" script files and warns about others. And setting it to on always sources.

So that's a change of behavior in warn which seems the right choice, but I don't see that change documented anywhere in this PR. There should be something in the help string for the setting.

I was on the fence between changing the behavior of warn and introducing a new value and changing the default. I ended up going with the latter to give users more control, plus it provided a natural place to document the new behavior. I've also added a release note about the new default.

@JDevlieghere JDevlieghere left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -56,7 +56,8 @@ enum InlineStrategy {
enum LoadScriptFromSymFile {
eLoadScriptFromSymFileTrue,
eLoadScriptFromSymFileFalse,
eLoadScriptFromSymFileWarn
eLoadScriptFromSymFileWarn,
eLoadScriptFromSymFileTrusted,

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm open to different/more specific naming. I went with "trusted" to cover both the code sign scenario as well as the concept of having default trusted paths, but we could certainly separate the two and have something like eLoadScriptFromSymFileSigned and eLoadScriptFromSymFileStandardLocation or something.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to distinguish between "system trusted" binaries and 3rd party/ad-hoc signed binaries ?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re separate: Are there systems/circumstances where a user might want both Signed and StandardLocation?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is starting to feel more like a bit mask. I might want "trusted and no others" or "trusted and warn about the others" for instance.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@medismailben Ad-hoc signed means nothing, so I don't think that's worth having as an option. In theory I can see value in distinguishing between Apple signed vs trusted 3rd party signed, but in practice I think it's simpler to trust what the system is trusting, which is what's currently implemented.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jimingham Flags would be nice, but AFAIK there's no way to specify flags as a setting.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we simulate it why an array/list of enum values?

@@ -56,7 +56,8 @@ enum InlineStrategy {
enum LoadScriptFromSymFile {
eLoadScriptFromSymFileTrue,
eLoadScriptFromSymFileFalse,
eLoadScriptFromSymFileWarn
eLoadScriptFromSymFileWarn,
eLoadScriptFromSymFileTrusted,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we want to distinguish between "system trusted" binaries and 3rd party/ad-hoc signed binaries ?

Comment thread lldb/source/Core/Module.cpp Outdated
Comment thread lldb/source/Target/Target.cpp Outdated
@github-actions

github-actions Bot commented Mar 30, 2026

Copy link
Copy Markdown

⚠️ C/C++ code formatter, clang-format found issues in your code. ⚠️

You can test this locally with the following command:
git-clang-format --diff origin/main HEAD --extensions h,c,cpp -- lldb/test/API/macosx/dsym_codesign/main.c lldb/include/lldb/Host/macosx/HostInfoMacOSX.h lldb/include/lldb/Target/Platform.h lldb/include/lldb/Target/Target.h lldb/source/Core/ModuleList.cpp lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.cpp lldb/source/Plugins/Platform/MacOSX/PlatformDarwin.h lldb/source/Target/Platform.cpp lldb/source/Target/Target.cpp --diff_from_common_commit

⚠️
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing origin/main to the base branch/commit you want to compare against.
⚠️

View the diff from clang-format here.
diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp
index 34d609bb2..42087a16c 100644
--- a/lldb/source/Core/ModuleList.cpp
+++ b/lldb/source/Core/ModuleList.cpp
@@ -1391,7 +1391,7 @@ bool ModuleList::LoadScriptingResourceInTargetForModule(Module &module,
     case eLoadScriptFromSymFileWarn:
       debugger.ReportWarning(
           llvm::formatv(
-      // clang-format off
+              // clang-format off
 R"('{0}' contains a debug script. To run this script in this debug session:
 
     command script import "{1}"
@@ -1400,7 +1400,7 @@ To run all discovered debug scripts in this session:
 
     settings set target.load-script-from-symbol-file true
 )",
-      // clang-format on
+              // clang-format on
               module.GetFileSpec().GetFileNameStrippingExtension(),
               scripting_fspec.GetPath()),
           debugger.GetID());

@@ -4379,6 +4368,12 @@ static constexpr OptionEnumValueElement g_load_script_from_sym_file_values[] = {
"warn",
"Warn about debug scripts inside symbol files but do not load them.",
},
{
eLoadScriptFromSymFileTrusted,
"trusted",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably update (or create) some docs describing how this relates to "safe paths". E.g., symbol files from "safe paths" aren't currently trusted in the sense of this setting. I guess one way we could think about the distinction is that LLDB is willing to consider loading from safe-paths, but the actual loading is condition on the target.load-script-from-symbol-file?

eLoadScriptFromSymFileTrusted,
"trusted",
"Load debug scripts inside trusted symbol files, and warn about "
"scripts from untrusted symbol files.",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The warn setting is rather niche now. Should we consider claiming it deprecated? Unless we want to be able to say "don't load trusted files but do warn". OTOH it doesn't look like much maintenance.

There's some logic in Debugger.cpp that handles reloading of the scripts when the setting gets changed. You might want to add a case for the new enum there too. But feel free to look at that separate from this PR. I'm not sure we have any tests for that

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it's important to have a "show me all the things you would load but load none of them" mode. That will help people who are getting used to what all the author of the extensions intends to do. Maybe that's the appropriate use of warn?

@Michael137 Michael137 Mar 31, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea that's a good point. Though currently when we warn we bail. We should change that to continue if we want the dry-run semantics. I was planning on doing that for one of my PRs anyway. Happy to keep the warn

@JDevlieghere

Copy link
Copy Markdown
Member Author

Seems like we want to change the semantics slightly to cover the following scenarios:

  1. eLoadScriptFromSymFileFalse: Load nothing (unchanged)
  2. eLoadScriptFromSymFileTrue: Load everything (unchanged)
  3. eLoadScriptFromSymFileWarn: Load nothing but tell me what you would have loaded (unchanged) and show what would have been loaded (new)
  4. eLoadScriptFromSymFileSafe: Load everything safe and don't warn about what hasn't been loaded (new default)

Let me know if this works for everyone. Since Michael and I are both changing the same code, I'm going to limit this PR to implementing (4) and defer (3) to another PR, together with updated documentation as @Michael137 suggested.

@jimingham

Copy link
Copy Markdown
Contributor

Seems like we want to change the semantics slightly to cover the following scenarios:

  1. eLoadScriptFromSymFileFalse: Load nothing (unchanged)
  2. eLoadScriptFromSymFileTrue: Load everything (unchanged)
  3. eLoadScriptFromSymFileWarn: Load nothing but tell me what you would have loaded (unchanged) and show what would have been loaded (new)

It would also be handy for Warn to distinguish between safe and unsafe when it lists the what would have been loaded. This way I can tell from the output of the warnings, what I would get with 4 & what I would get with 1.

  1. eLoadScriptFromSymFileSafe: Load everything safe and don't warn about what hasn't been loaded (new default)

Let me know if this works for everyone. Since Michael and I are both changing the same code, I'm going to limit this PR to implementing (4) and defer (3) to another PR, together with updated documentation as @Michael137 suggested.

@JDevlieghere

Copy link
Copy Markdown
Member Author

Rebased on Michael's PRs.

Comment thread lldb/source/Core/Module.cpp Outdated
Comment on lines +1479 to +1480
if (!platform_sp->IsSymbolFileTrusted(*this))
continue;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I might've missed where we landed on things in the end, but this means we won't tell the user about not having loaded scripts right? I don't have a strong opinion on that, just wanted to clarify

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that was Jim's suggestion that became (4) in #189444 (comment)

LLDB automatically discovers, but doesn't automatically load, scripts in
the dSYM bundle. This is to prevent running untrusted code. Users can
choose to import the script manually or toggle a global setting to
override this policy. This isn't a great user experience: the former
quickly becomes tedious and the latter leads to decreased security.

This PR offers a middle ground that allows LLDB to automatically load
scripts from trusted dSYM bundles. Trusted here means that the bundle
was signed  with a certificate trusted by the system. This can be a
locally created certificate (but not an ad-hoc certificate) or a
certificate from a trusted vendor.
@JDevlieghere
JDevlieghere force-pushed the trust-codesigned-dsyms branch from c23dcf2 to 7ac8bfe Compare April 3, 2026 16:57
@JDevlieghere
JDevlieghere enabled auto-merge (squash) April 3, 2026 16:57
@JDevlieghere
JDevlieghere merged commit 271a088 into llvm:main Apr 3, 2026
10 of 12 checks passed
JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Apr 3, 2026
I had auto-merge enabled in llvm#189444 and since the formatter is
non-blocking it got merged despite the issue. Given I'm already here, I
just formatted the whole file.
JDevlieghere added a commit that referenced this pull request Apr 3, 2026
I had auto-merge enabled in #189444 and since the formatter is
non-blocking it got merged despite the issue. Given I'm already here, I
just formatted the whole file.
@llvm-ci

llvm-ci commented Apr 3, 2026

Copy link
Copy Markdown

LLVM Buildbot has detected a new failure on builder sanitizer-x86_64-linux-android running on sanitizer-buildbot-android while building lldb,llvm at step 2 "annotate".

Full details are available at: https://lab.llvm.org/buildbot/#/builders/186/builds/17589

Here is the relevant piece of the build log for the reference
Step 2 (annotate) failure: 'python ../sanitizer_buildbot/sanitizers/zorg/buildbot/builders/sanitizers/buildbot_selector.py' (failure)
...
[ RUN      ] AddressSanitizerInterface.GlobalRedzones
[       OK ] AddressSanitizerInterface.GlobalRedzones (0 ms)
[ DISABLED ] AddressSanitizerInterface.DISABLED_StressLargeMemset
[ DISABLED ] AddressSanitizerInterface.DISABLED_StressSmallMemset
[ DISABLED ] AddressSanitizerInterface.DISABLED_InvalidPoisonAndUnpoisonCallsTest
[----------] 3 tests from AddressSanitizerInterface (0 ms total)

[----------] 1 test from AddressSanitizerInternalInterface
[ RUN      ] AddressSanitizerInternalInterface.SetShadow
[       OK ] AddressSanitizerInternalInterface.SetShadow (0 ms)
[----------] 1 test from AddressSanitizerInternalInterface (0 ms total)

[----------] 19 tests from AddressSanitizer
[ RUN      ] AddressSanitizer.LargeOOBInMemset
[       OK ] AddressSanitizer.LargeOOBInMemset (193 ms)
[ RUN      ] AddressSanitizer.BCmpOOBTest
[       OK ] AddressSanitizer.BCmpOOBTest (0 ms)
[ RUN      ] AddressSanitizer.OOB_int
[       OK ] AddressSanitizer.OOB_int (7727 ms)
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBLeftLow
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBLeftHigh
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBRightLow
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBRightHigh
[ RUN      ] AddressSanitizer.StrDupOOBTest
[       OK ] AddressSanitizer.StrDupOOBTest (690 ms)
[ RUN      ] AddressSanitizer.StrChrAndIndexOOBTest
[       OK ] AddressSanitizer.StrChrAndIndexOOBTest (715 ms)
[ RUN      ] AddressSanitizer.StrNCmpOOBTest
[       OK ] AddressSanitizer.StrNCmpOOBTest (1372 ms)
[ RUN      ] AddressSanitizer.StrArgsOverlapTest
[       OK ] AddressSanitizer.StrArgsOverlapTest (2803 ms)
[ RUN      ] AddressSanitizer.StrtolOverflow
[       OK ] AddressSanitizer.StrtolOverflow (0 ms)
[ RUN      ] AddressSanitizer.CallocTest
[       OK ] AddressSanitizer.CallocTest (0 ms)
[ DISABLED ] AddressSanitizer.DISABLED_TSDTest
[ RUN      ] AddressSanitizer.UAF_Packed5
[       OK ] AddressSanitizer.UAF_Packed5 (486 ms)
[ RUN      ] AddressSanitizer.WildAddressTest
[       OK ] AddressSanitizer.WildAddressTest (236 ms)
[ RUN      ] AddressSanitizer.ManyThreadsTest

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild


@@@STEP_FAILURE@@@

@@@STEP_FAILURE@@@

@@@STEP_FAILURE@@@
Step 34 (run instrumented asan tests [aarch64/bluejay-userdebug/TQ3A.230805.001]) failure: run instrumented asan tests [aarch64/bluejay-userdebug/TQ3A.230805.001] (failure)
...
[       OK ] AddressSanitizerInterface.OverlappingPoisonMemoryRegionTest (0 ms)
[ RUN      ] AddressSanitizerInterface.GlobalRedzones
[       OK ] AddressSanitizerInterface.GlobalRedzones (0 ms)
[ DISABLED ] AddressSanitizerInterface.DISABLED_StressLargeMemset
[ DISABLED ] AddressSanitizerInterface.DISABLED_StressSmallMemset
[ DISABLED ] AddressSanitizerInterface.DISABLED_InvalidPoisonAndUnpoisonCallsTest
[----------] 3 tests from AddressSanitizerInterface (0 ms total)

[----------] 1 test from AddressSanitizerInternalInterface
[ RUN      ] AddressSanitizerInternalInterface.SetShadow
[       OK ] AddressSanitizerInternalInterface.SetShadow (0 ms)
[----------] 1 test from AddressSanitizerInternalInterface (0 ms total)

[----------] 19 tests from AddressSanitizer
[ RUN      ] AddressSanitizer.LargeOOBInMemset
[       OK ] AddressSanitizer.LargeOOBInMemset (193 ms)
[ RUN      ] AddressSanitizer.BCmpOOBTest
[       OK ] AddressSanitizer.BCmpOOBTest (0 ms)
[ RUN      ] AddressSanitizer.OOB_int
[       OK ] AddressSanitizer.OOB_int (7727 ms)
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBLeftLow
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBLeftHigh
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBRightLow
[ DISABLED ] AddressSanitizer.DISABLED_DemoOOBRightHigh
[ RUN      ] AddressSanitizer.StrDupOOBTest
[       OK ] AddressSanitizer.StrDupOOBTest (690 ms)
[ RUN      ] AddressSanitizer.StrChrAndIndexOOBTest
[       OK ] AddressSanitizer.StrChrAndIndexOOBTest (715 ms)
[ RUN      ] AddressSanitizer.StrNCmpOOBTest
[       OK ] AddressSanitizer.StrNCmpOOBTest (1372 ms)
[ RUN      ] AddressSanitizer.StrArgsOverlapTest
[       OK ] AddressSanitizer.StrArgsOverlapTest (2803 ms)
[ RUN      ] AddressSanitizer.StrtolOverflow
[       OK ] AddressSanitizer.StrtolOverflow (0 ms)
[ RUN      ] AddressSanitizer.CallocTest
[       OK ] AddressSanitizer.CallocTest (0 ms)
[ DISABLED ] AddressSanitizer.DISABLED_TSDTest
[ RUN      ] AddressSanitizer.UAF_Packed5
[       OK ] AddressSanitizer.UAF_Packed5 (486 ms)
[ RUN      ] AddressSanitizer.WildAddressTest
[       OK ] AddressSanitizer.WildAddressTest (236 ms)
[ RUN      ] AddressSanitizer.ManyThreadsTest

How to reproduce locally: https://github.com/google/sanitizers/wiki/SanitizerBotReproduceBuild




program finished with exit code 0
elapsedTime=2204.901873

JDevlieghere added a commit to JDevlieghere/llvm-project that referenced this pull request Apr 3, 2026
This patch does two thing:

- It reverts to the previous behavior of warning for untrusted dSYMs.
- It includes whether a dSYM is trusted or untrusted in the warning
  output.

My reasoning is that there's no tooling for automatically signing dSYMs
and therefore we shouldn't change the behavior until this is more
common. The inclusion of whether the dSYM is signed or not is the first
step towards advertising the existence of the feature.

This now also means the release note I added in llvm#189444 is correct
(again).
JDevlieghere added a commit that referenced this pull request Apr 3, 2026
This patch does two thing:

- It reverts to the previous behavior of warning for untrusted dSYMs.
- It includes whether a dSYM is trusted or untrusted in the warning
output.

My reasoning is that there's no tooling for automatically signing dSYMs
and therefore we shouldn't change the behavior until this is more
common. The inclusion of whether the dSYM is signed or not is the first
step towards advertising the existence of the feature.

This now also means the release note I added in #189444 is correct
(again).
Michael137 added a commit to Michael137/llvm-project that referenced this pull request Apr 3, 2026
This patch is a follow-up to llvm#189444 (comment)

We want `eLoadScriptFromSymFileWarn` to be a "dry-run" mode which warns but all possible module loads but doesn't actually load them. To do so, this patch removes the short-circuit in the current module loading loop.

The previous warning is verbose and contains instructions that don't need to be printed for every module. So this patch ensures LLDB only prints the verbose warning once per debugger-session. The script paths that would have been loaded are accumulated and printed at the end of the function. We `return false` to preserve the previous semantics of `LoadScriptingResourceInTarget`.

Eventually we want the warning to also indicate whether the module in consideration is a safe/trusted module or not but I wanted to keep that for a separate PR.
Michael137 pushed a commit to swiftlang/llvm-project that referenced this pull request Apr 8, 2026
LLDB automatically discovers, but doesn't automatically load, scripts in
the dSYM bundle. This is to prevent running untrusted code. Users can
choose to import the script manually or toggle a global setting to
override this policy. This isn't a great user experience: the former
quickly becomes tedious and the latter leads to decreased security.

This PR offers a middle ground that allows LLDB to automatically load
scripts from trusted dSYM bundles. Trusted here means that the bundle
was signed with a certificate trusted by the system. This can be a
locally created certificate (but not an ad-hoc certificate) or a
certificate from a trusted vendor.

(cherry picked from commit 271a088)
Michael137 pushed a commit to swiftlang/llvm-project that referenced this pull request Apr 8, 2026
This patch does two thing:

- It reverts to the previous behavior of warning for untrusted dSYMs.
- It includes whether a dSYM is trusted or untrusted in the warning
output.

My reasoning is that there's no tooling for automatically signing dSYMs
and therefore we shouldn't change the behavior until this is more
common. The inclusion of whether the dSYM is signed or not is the first
step towards advertising the existence of the feature.

This now also means the release note I added in llvm#189444 is correct
(again).

(cherry picked from commit 52568a5)
zwu-2025 pushed a commit to zwu-2025/llvm-project that referenced this pull request May 17, 2026
LLDB automatically discovers, but doesn't automatically load, scripts in
the dSYM bundle. This is to prevent running untrusted code. Users can
choose to import the script manually or toggle a global setting to
override this policy. This isn't a great user experience: the former
quickly becomes tedious and the latter leads to decreased security.

This PR offers a middle ground that allows LLDB to automatically load
scripts from trusted dSYM bundles. Trusted here means that the bundle
was signed with a certificate trusted by the system. This can be a
locally created certificate (but not an ad-hoc certificate) or a
certificate from a trusted vendor.
zwu-2025 pushed a commit to zwu-2025/llvm-project that referenced this pull request May 17, 2026
I had auto-merge enabled in llvm#189444 and since the formatter is
non-blocking it got merged despite the issue. Given I'm already here, I
just formatted the whole file.
zwu-2025 pushed a commit to zwu-2025/llvm-project that referenced this pull request May 17, 2026
This patch does two thing:

- It reverts to the previous behavior of warning for untrusted dSYMs.
- It includes whether a dSYM is trusted or untrusted in the warning
output.

My reasoning is that there's no tooling for automatically signing dSYMs
and therefore we shouldn't change the behavior until this is more
common. The inclusion of whether the dSYM is signed or not is the first
step towards advertising the existence of the feature.

This now also means the release note I added in llvm#189444 is correct
(again).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants